package cn.snmp.mutual.nms;

import java.io.*;
import java.util.*;

public class ASN1Parser implements Serializable
{
    byte[] data;
    int m_idx;
    int spaceLeft;
    int bookMark;
    int dataLength;
    int requiredSize;
    boolean DEBUG;
    boolean useDec;
    byte[] d_data;
    
    ASN1Parser() {
        this.requiredSize = 1024;
        this.DEBUG = false;
        this.useDec = false;
        this.d_data = null;
        this.data = new byte[this.requiredSize];
        final int length = this.data.length;
        this.spaceLeft = length;
        this.dataLength = length;
        this.bookMark = 0;
    }
    
    ASN1Parser(final byte[] abyte0) {
        this.requiredSize = 1024;
        this.DEBUG = false;
        this.useDec = false;
        this.d_data = null;
        this.data = abyte0;
        final int length = this.data.length;
        this.spaceLeft = length;
        this.dataLength = length;
        this.bookMark = 0;
        this.m_idx = 0;
    }
    
    boolean createSpace() {
        if (this.dataLength >= 65536) {
            return false;
        }
        final int n = this.dataLength * 2;
        this.dataLength = n;
        this.spaceLeft = n;
        return true;
    }
    
    public int decodeInteger() throws ArrayIndexOutOfBoundsException {
        int i = this.decodeLength();
        int j = this.data[this.m_idx++];
        while (--i > 0) {
            j = (j << 8 | (this.data[this.m_idx++] & 0xFF));
        }
        return j;
    }
    
    public int decodeLength() throws ArrayIndexOutOfBoundsException {
        int i = this.data[this.m_idx++];
        if (i < 0) {
            int j = i & 0x7F;
            i = 0;
            while (j-- > 0) {
                i = (i << 8 | (this.data[this.m_idx++] & 0xFF));
            }
        }
        return i;
    }
    
    public long[] decodeLong() throws ArrayIndexOutOfBoundsException {
        int i = this.decodeLength();
        final long[] al = new long[2];
        if (i > 4) {
            al[1] = this.data[this.m_idx++];
            while (--i > 4) {
                al[1] = (al[1] << 8 | (long)(this.data[this.m_idx++] & 0xFF));
            }
            al[1] &= 0xFFFFFFFFL;
        }
        while (i > 0) {
            al[0] = (al[0] << 8 | (long)(this.data[this.m_idx++] & 0xFF));
            --i;
        }
        return al;
    }
    
    public int[] decodeOID() throws ArrayIndexOutOfBoundsException {
        final int i = this.decodeLength();
        final int j = this.m_idx + i;
        if (i == 0) {
            final int[] ai = new int[2];
            ai[0] = (ai[1] = 0);
            return ai;
        }
        int k = i + 1;
        for (int l = i, i2 = this.m_idx; l > 0; --l, ++i2) {
            if (this.data[i2] < 0) {
                --k;
            }
        }
        final int[] ai2 = new int[k];
        int j2 = this.data[this.m_idx++];
        if (j2 < 0) {
            j2 &= 0x7F;
            byte byte0;
            do {
                byte0 = this.data[this.m_idx++];
                j2 = (j2 << 7 | (byte0 & 0x7F));
            } while (byte0 < 0);
        }
        ai2[0] = j2 / 40;
        ai2[1] = j2 % 40;
        int l2 = 2;
        while (this.m_idx < j) {
            int k2 = this.data[this.m_idx++];
            if (k2 < 0) {
                k2 &= 0x7F;
                byte byte2;
                do {
                    byte2 = this.data[this.m_idx++];
                    k2 = (k2 << 7 | (byte2 & 0x7F));
                } while (byte2 < 0);
            }
            ai2[l2] = k2;
            ++l2;
        }
        return ai2;
    }
    
    public byte[] decodeOctetString() throws ArrayIndexOutOfBoundsException {
        final int i = this.decodeLength();
        final byte[] abyte0 = new byte[i];
        System.arraycopy(this.data, this.m_idx, abyte0, 0, i);
        this.m_idx += i;
        return abyte0;
    }
    
    public void decodeVarBindList(final Vector vector) throws SnmpException {
        final int i = this.decodeLength();
        final int j = this.m_idx + i;
        while (this.m_idx < j) {
            if (!this.isMessageSequence()) {
                throw new SnmpException(SnmpUtils.getString("Parse Auth: Incorrect VarBind"));
            }
            this.decodeLength();
            final SnmpVarBind snmpvarbind = new SnmpVarBind();
            if (!this.isOID()) {
                throw new SnmpException(SnmpUtils.getString("Parse Error: Decoding enterprise."));
            }
            snmpvarbind.setObjectID(SnmpOID.newInstance(this.decodeOID()));
            final SnmpVar snmpvar = this.decodeVariable();
            if (snmpvar instanceof SnmpNull) {
                snmpvarbind.setErrIndex(snmpvar.varbindErrorVal & 0xFF);
            }
            snmpvarbind.setVariable(snmpvar);
            vector.addElement(snmpvarbind);
        }
    }
    
    SnmpVar decodeVariable() throws SnmpException {
        Object obj = null;
        final byte byte0 = this.getTag();
        switch (byte0) {
            case 2: {
                obj = new SnmpInt(this.decodeInteger());
                break;
            }
            case 4: {
                obj = SnmpString.newInstance(this.decodeOctetString());
                break;
            }
            case 6: {
                obj = SnmpOID.newInstance(this.decodeOID());
                break;
            }
            case 5: {
                ++this.m_idx;
                obj = new SnmpNull();
                break;
            }
            case 64: {
                obj = SnmpIpAddress.newInstance(this.decodeOctetString());
                break;
            }
            case 65: {
                obj = new SnmpCounter((long)this.decodeInteger() & 0xFFFFFFFFL);
                break;
            }
            case 66: {
                obj = new SnmpGauge((long)this.decodeInteger() & 0xFFFFFFFFL);
                break;
            }
            case 67: {
                obj = new SnmpTimeticks((long)this.decodeInteger() & 0xFFFFFFFFL);
                break;
            }
            case 68: {
                obj = SnmpOpaque.newInstance(this.decodeOctetString());
                break;
            }
            case 71: {
                obj = new SnmpUnsignedInt((long)this.decodeInteger() & 0xFFFFFFFFL);
                break;
            }
            case 3: {
                obj = SnmpBitstring.newInstance(this.decodeOctetString());
                break;
            }
            case 70: {
                obj = new SnmpCounter64(this.decodeLong());
                break;
            }
            case 69: {
                obj = SnmpNsap.newInstance(this.decodeOctetString());
                break;
            }
            case Byte.MIN_VALUE: {
                obj = new SnmpNull((byte)(-128));
                ++this.m_idx;
                break;
            }
            case -127: {
                obj = new SnmpNull((byte)(-127));
                ++this.m_idx;
                break;
            }
            case -126: {
                obj = new SnmpNull((byte)(-126));
                ++this.m_idx;
                break;
            }
            default: {
                throw new SnmpException(SnmpUtils.getString("Cannot decode ASN Type: ") + " " + Integer.toString(byte0, 16));
            }
        }
        return (SnmpVar)obj;
    }
    
    static SnmpVar decodeVariable(final byte[] abyte0) throws SnmpException {
        final ASN1Parser asn1parser = new ASN1Parser(abyte0);
        try {
            return asn1parser.decodeVariable();
        }
        catch (ArrayIndexOutOfBoundsException _ex) {
            throw new SnmpException(SnmpUtils.getString("Error decoding variable"));
        }
    }
    
    public void encodeCommand(final byte byte0) {
        this.spaceLeft = ASNTypes.encodeSequence(this.data, this.spaceLeft, this.dataLength - this.spaceLeft, byte0);
    }
    
    public void encodeEnterprise(final SnmpOID snmpoid) {
        this.spaceLeft = snmpoid.encode(this.data, this.spaceLeft);
    }
    
    public void encodeIPAddress(final SnmpString snmpstring) {
        this.spaceLeft = ASNTypes.encodeString(this.data, this.spaceLeft, (String)snmpstring.toValue(), 0, 4, 64);
    }
    
    public void encodeInt(final int i) {
        this.spaceLeft = ASNTypes.encodeInt(this.data, this.spaceLeft, i, 2);
    }
    
    public void encodeOctets(final byte[] abyte0, final int i) {
        this.spaceLeft = ASNTypes.encodeOctets(this.data, this.spaceLeft, abyte0, 0, i, 4);
    }
    
    public void encodeSequence() {
        this.spaceLeft = ASNTypes.encodeSequence(this.data, this.spaceLeft, this.dataLength - this.spaceLeft, 48);
    }
    
    public void encodeSequence(final byte byte0) {
        this.spaceLeft = ASNTypes.encodeSequence(this.data, this.spaceLeft, this.dataLength - this.spaceLeft, byte0);
    }
    
    public void encodeSequence(final int i) {
        this.spaceLeft = ASNTypes.encodeSequence(this.data, this.spaceLeft, i - this.spaceLeft, 48);
    }
    
    public void encodeSequence(final int i, final byte byte0) {
        this.spaceLeft = ASNTypes.encodeSequence(this.data, this.spaceLeft, i - this.spaceLeft, byte0);
    }
    
    public void encodeString(final String s) {
        this.spaceLeft = ASNTypes.encodeString(this.data, this.spaceLeft, s, 0, (s == null) ? 0 : s.length(), 4);
    }
    
    public void encodeUInt(final int i) {
        this.spaceLeft = ASNTypes.encodeUInt(this.data, this.spaceLeft, i, 67);
    }
    
    public void encodeVarBindList(final Vector vector) {
        int i = vector.size() - 1;
        final int j = this.dataLength;
        while (i >= 0) {
            final SnmpVarBind snmpvarbind = (SnmpVarBind)vector.elementAt(i);
            this.spaceLeft = snmpvarbind.encode(this.data, this.spaceLeft);
            final int k = this.spaceLeft;
            --i;
        }
    }
    
    public int getBookMark() {
        return this.bookMark;
    }
    
    public byte[] getData() {
        return this.data;
    }
    
    public int getIndex() {
        return this.m_idx;
    }
    
    public int getPosition() {
        return this.dataLength - this.spaceLeft;
    }
    
    public byte[] getReceivedData() {
        if (this.useDec) {
            return this.d_data;
        }
        return this.data;
    }
    
    public byte getTag() {
        return this.data[this.m_idx++];
    }
    
    void init() {
        if (this.requiredSize > this.dataLength) {
            this.data = new byte[this.requiredSize];
        }
        else {
            this.data = new byte[this.dataLength];
        }
        final int length = this.data.length;
        this.spaceLeft = length;
        this.dataLength = length;
        this.bookMark = 0;
        this.m_idx = 0;
    }
    
    public boolean isIPAddress() {
        return this.data[this.m_idx++] == 64;
    }
    
    public boolean isInteger() {
        return this.data[this.m_idx++] == 2;
    }
    
    public boolean isMessageSequence() {
        return this.data[this.m_idx++] == 48;
    }
    
    public boolean isOID() {
        return this.data[this.m_idx++] == 6;
    }
    
    public boolean isOctetString() {
        return this.data[this.m_idx++] == 4;
    }
    
    public boolean isTimeTicks() {
        return this.data[this.m_idx++] == 67;
    }
    
    void prepareToSend() {
        final byte[] abyte0 = new byte[this.dataLength - this.spaceLeft];
        System.arraycopy(this.data, this.spaceLeft, abyte0, 0, this.dataLength - this.spaceLeft);
        this.data = abyte0;
        this.dataLength = this.data.length;
        this.spaceLeft = 0;
    }
    
    void print() {
        System.out.println(SnmpUtils.getString("dataLength") + " = " + this.dataLength);
        System.out.println(SnmpUtils.getString("spaceLeft") + " = " + this.spaceLeft);
        System.out.println(SnmpUtils.getString("data.length") + " = " + this.data.length);
    }
    
    public void setBookMark() {
        this.bookMark = this.spaceLeft;
    }
    
    void setDebug(final boolean flag) {
        this.DEBUG = flag;
    }
}
